package com.example.springsecuritydemo.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

import static org.springframework.security.config.Customizer.withDefaults;

@Configuration //配置类

//@EnableWebSecurity注解  启用Spring Security 的自定义配置 ，这是Spring boot 的默认配置类(可选)，如果项目是Spring Boot 项目，可以忽略此注解。
//单纯的 Spring 项目必须添加此注解，否则Spring Security 将不会启用。
//@EnableWebSecurity
/**
 * 配置 Web 安全的配置类，用于设置数据库中的用户认证信息。
 */
@EnableMethodSecurity //启用方法授权
public class WebSecurityConfig {

    /**
     * 配置密码编码器，使用BCrypt加密算法
     *
     * @return BCryptPasswordEncoder实例
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //如果想要开启，注释掉csrf就开启csrf攻击防御了。
        http.csrf(csrf -> csrf.disable()); //关闭csrf， 实际项目中通常不应禁用CSRF，开启scrf,要求前端页面会传递隐藏的标签csrf，在后端校验，防止跨站请求伪造攻击。

        //跨域配置
        http.cors(withDefaults());

        // 注册认证提供者
        // http.authenticationProvider(authenticationProvider);
        //开启授权保护，这个配置控制了过滤器链中都有那些过滤器，以及过滤器中的一些细节
        http.authorizeHttpRequests(
                authoriza -> authoriza
                        //配置权限 有管理员权限才可以访问 users/**
                        //.requestMatchers(new AntPathRequestMatcher("/users/getUserList")).hasAuthority("USER_LIST")
                        //.requestMatchers(new AntPathRequestMatcher("/users/selectOneUser/**")).hasAuthority("USER_SELECT")
                        //.requestMatchers(new AntPathRequestMatcher("/users/**")).hasRole("USER")
                        //.requestMatchers(new AntPathRequestMatcher("/index")).hasRole("USER")
                        

                        // 允许访问Swagger UI相关路径
                        //.antMatchers("/swagger-ui/**", "/v3/api-docs/**", "/swagger-ui.html").permitAll() //可以先使用已有的账号登录，登录成功后，再访问swagger-ui去调试
                        .anyRequest() //所有的请求都允许访问
                        .authenticated() // 所有其他请求需要认证
        );
        //http.httpBasic(withDefaults());//使用HTTP基本授权方式，浏览器自带的登录表单，没有登出页面
        //.formLogin(withDefaults())//自动使用表单授权方式，默认使用表单登录方式和登出页面
        //认证
        http.formLogin(form -> {
            form.loginPage("/login/toLogin")
                    .permitAll()//自定义登录页面，无需要认证
                    .usernameParameter("username") //配置自定义的用户名参数，默认为username
                    .passwordParameter("password") //配置自定义的密码参数，默认为password
                    .failureUrl("/login?error") //登录失败后跳转的页面
                    .successHandler(new MyAuthenticationSuccessHandler()) //认证成功时的处理器
                    .failureHandler(new MyAuthenticationFailureHandler()) //认证失败时的处理器

            ; //配置自定义的密码参数，默认为password
        });

        //登出
        http.logout(logout -> {
            logout.logoutSuccessHandler(new MyLogoutSuccessHandler()); //注销成功后的处理器
        });//自定义登录页面

        //自定义认证失败处理器
        http.exceptionHandling(exception -> {
            exception.authenticationEntryPoint(new MyAuthenticationEntryPoint()); //自定义认证失败处理器
            exception.accessDeniedHandler(new MyAccessDeniedHandler()); //自定义访问拒绝处理器
        });

        //自定义会话过期处理器
        http.sessionManagement(session -> {
            session.maximumSessions(1) //设置最大会话数，超过限制的会话将被拒绝，
                    //.maxSessionsPreventsLogin(true) //设置是否阻止新的登录，true表示阻止，false表示允许
                    .expiredSessionStrategy(new HySessionInformationExpiredStrategy());//同一个用户只能有一个会话，其他会话将被拒绝，后登录的会挤掉旧会话

        });


        return http.build();
    }


}